package nfa;

import fa.FARule;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author haoly
 * @title NFARulebook
 * @date 2025/4/16 14:14
 * @description NFARulebook
 */
public class NFARulebook<T> {
    private final List<FARule<T>> rules;

    public NFARulebook(List<FARule<T>> rules) {
        this.rules = rules;
    }

    public List<FARule<T>> getRules() {
        return rules;
    }

    public Set<T> nextStates(Set<T> states, Character character){
        Set<T> result = new HashSet<>();
        for (T state : states) {
            Set<T> nextStates = followRulesFor(state, character);
            if (nextStates != null && nextStates.size() > 0) {
                result.addAll(nextStates);
            }
        }
        return result;
    }

    public Set<T> followRulesFor(T state, Character character) {
        Set<T> result = new HashSet<>();
        List<FARule<T>> rules = rulesFor(state, character);
        for (FARule<T> rule : rules) {
            if (rule != null) {
                result.add(rule.follow());
            }
        }
        return result;
    }

    public List<FARule<T>> rulesFor(T state, Character character) {
        List<FARule<T>> result = new ArrayList<>();
        for (FARule<T> rule : this.rules) {
            if (rule.appliesTo(state, character)) {
                result.add(rule);
            }
        }
        return result;
    }


    public Set<T> followFreeMoves(Set<T> states){
        Set<T> resultSet = new HashSet<>(states);
        Set<T> moreStates = nextStates(resultSet, null);
        if (states.containsAll(moreStates)) {
            return states;
        }else {
            resultSet.addAll(moreStates);
            return followFreeMoves(resultSet);
        }
    }

    public Set<Character> alphabet(){
        Set<Character> result = new HashSet<>();
        for (FARule<T> rule : rules) {
            Character character = rule.getCharacter();
            if (character != null) {
                result.add(character);
            }
        }
        return result;
    }


}
